package cs342.project1.mathNotebook.expressions;

import cs342.project1.mathNotebook.expressions.exceptions.UncapturedVariableException;

public class EvaluationVisitor implements Visitor {

    private double value;
    private String variable;

    /**
     * Constructs a new Evaluation visitor.
     * 
     * @param variable
     *            the variable to evaluate in this expression
     * @param value
     *            the value to put in place of the variable
     */
    public EvaluationVisitor(String variable, double value) {
	this.value = value;
	this.variable = variable;
    }

    // TODO: Check that value & variable are not null
    public EvaluationVisitor() {
    }

    public Double visit(Expr exp) {
	throw new IllegalArgumentException(
		"Cannot evaluation an instance of the abstract class Expression");
    }

    @Override
    public Double visit(AddExpr exp) throws UncapturedVariableException {
	return (Double) exp.lhs.accept(this) + (Double) exp.rhs.accept(this);
    }

    public Double visit(SubExpr exp) throws UncapturedVariableException {
	return (Double) exp.lhs.accept(this) - (Double) exp.rhs.accept(this);
    }

    public Double visit(MultExpr exp) throws UncapturedVariableException {
	return (Double) exp.lhs.accept(this) * (Double) exp.rhs.accept(this);
    }

    public Double visit(DivExpr exp) throws UncapturedVariableException {
	return (Double) exp.lhs.accept(this) / (Double) exp.rhs.accept(this);
    }

    public Double visit(SinExpr exp) throws UncapturedVariableException {
	return Math.sin((Double) exp.argument.accept(this));
    }

    public Double visit(CosExpr exp) throws UncapturedVariableException {
	return Math.cos((Double) exp.argument.accept(this));
    }

    public Double visit(TanExpr exp) throws UncapturedVariableException {
	return Math.tan((Double) exp.argument.accept(this));
    }

    public Double visit(VarExpr exp) throws UncapturedVariableException {
	if (exp.name != null && exp.name.equals(variable)) {
	    return this.value;
	} else {
	    throw new UncapturedVariableException(exp.name
		    + " is an uncaptured variable");
	}

    }

    public Double visit(NegExpr exp) throws UncapturedVariableException {
	return (Double) exp.argument.accept(this) * (-1.0);
    }

    public Double visit(LiteralExpr exp) {
	return (Double) exp.value;
    }

    @Override
    public Object visit(PowExpr exp) throws UncapturedVariableException {
	return Math.pow((Double) exp.lhs.accept(this),
		(Double) exp.rhs.accept(this));
    }
}